#define  _GNU_SOURCE
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <sys/un.h>
#include <signal.h>
#include <sys/signalfd.h>
#include <sys/sysinfo.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <sys/errno.h>
#include <sys/prctl.h>
#include <sys/stat.h>
#include <sys/mount.h>
#include <sys/reboot.h>

#include "controller.h"
#include "../src/config/config.h"
#include "../src/conn/conn.h"
#include "../src/core/core.h"


typedef struct
{
    FILE*       kmsg;
    int         epoll_fd;
    long        mtime;
    char        sock_path[1024];
    char        unit_path[1024];
    char        cgroup_path[1024];
    char        logm_path[1024];
    Target_t*   now;
    bootm_t*    bootm;
} init_t;

init_t initor; //rinitd对象


void pipe_handler(int sig){
    
    char msgbuf[1024];
    sprintf(msgbuf,"pid.(%d)",getpid());
    while (1)
    {
        printf("recv pipe signal retry connect log_server\n");
        int local_fd=fdhandle(initor.logm_path,msgbuf);
        if (local_fd>0)
        {
            dup2(local_fd,STDOUT_FILENO);
            dup2(local_fd,STDERR_FILENO);
            close(local_fd);
            break;
        }else{
            usleep(30000000);
        }
    }
}


//父进程接收子进程退出信号，回收进程资源
void sigcld_handler(struct signalfd_siginfo *info){
    pid_t pid;
    int status; 
    //printf("[\033[3;32m  ok  \033[0m] pid=%d sig=%d code=%d ptr=%p -- msg(%s)\n",info->_sifields.,info->si_signo,info->si_code,data,strerror(info->si_errno));
    //使用WNOHANG选项避免阻塞
    while ((pid=waitpid(-1,&status,WNOHANG))>0) //while回收多个进程
    {
        if (WIFEXITED(status)) //自身主动退出
        {
            int ecode;
            if ((ecode=WEXITSTATUS(status))==0){
                printf("[\033[3;32mrinitd\033[0m] child process pid=%d complated.\n",pid);
            }else{
                //添加重启策略
                
                printf("[\033[3;32mrinitd\033[0m] child process pid=%d exited. -- (from self ExitCode=%d)\n",pid,WEXITSTATUS(status));
            } 
            fflush(stdout);
        }else if (WIFSIGNALED(status)) //来自信号被动退出
        {
            printf("[\033[3;32mrinitd\033[0m] child process pid=%d exited. -- (from signal value=%d)\n",pid,WTERMSIG(status));
            fflush(stdout);
        }else
        {
            printf("[\033[3;32m  ok  \033[0m] child process pid=%d exited. -- (from other ExitCode=%d)\n",pid,status);
            fflush(stdout);
        }
    }
}




/*
1.start
2.stop
3.status
4.restart
5.disable
6.enable
*/
int cmd_action(int client_fd,head_t header){
    char buf[1024];
    if (header.magic==CONN_MAGIC_CMD && header.action==CONN_CMD_STATUS) //status
    {
        read(client_fd,buf,header.payloadlen);
        printf("status: %s %d\n",buf,header.payloadlen);
        return 0;
    }else if (header.magic==CONN_MAGIC_CMD && header.action==CONN_CMD_START) //start
    {
        //
        
        read(client_fd,buf,header.payloadlen);
        printf("start: %s \n",buf);
        return 0;
    }else if (header.magic==CONN_MAGIC_CMD && header.action==CONN_CMD_STOP) //stop
    {
        //kill(initor.now->tasks->status.pid,0);
        read(client_fd,buf,header.payloadlen);
        printf("stop: %s\n",buf);
        return 0;
    }else if (header.magic==CONN_MAGIC_CMD && header.action==CONN_CMD_SHUTDOWN) //shutdown
    {
        printf("shutdown\n");
        return 1;
    }
    return 0;
}

//加载配置
void load_initconf(void* conf,const char* section,const char* key,char* value){
    init_t *config=(init_t*)conf;
    if (strcmp(section,"rinitd")==0 && strcmp(key,"Socket")==0)
    {
        trim(value);
        strcpy(config->sock_path,value);
    }else if (strcmp(section,"rinitd")==0 && strcmp(key,"UnitPath")==0)
    {
        trim(value);
        strcpy(config->unit_path,value);
    }else if (strcmp(section,"rinitd")==0 && strcmp(key,"Kmsg")==0)
    {
        trim(value);
        if (strcmp(value,"off")==0)
        {
            config->kmsg=NULL;
        }else
        {
            config->kmsg=fopen("/dev/kmsg","a");
        }
    }else if (strcmp(section,"rinitd")==0 && strcmp(key,"Cgroup")==0)
    {
        trim(value);
        strcpy(config->cgroup_path,value);
    }
}



// run init
int run(){
    memset(&initor,0,sizeof(init_t));
    strcpy(initor.sock_path,"/run/rinitd/rinitd.sock");
    strcpy(initor.unit_path,"/root/rinitd/etc/rinitd");
    strcpy(initor.logm_path,"/run/logm.sock");
    initor.kmsg=NULL;
    // init_mountfs()
    if (access("/root/rinitd/etc/rinitd/conf/rinitd.conf",F_OK)==0)
    {
        ini_parse(&initor,"/root/rinitd/etc/rinitd/conf/rinitd.conf",load_initconf,&initor.mtime); //依赖/dev目录
    }

    //kmsg
    //FILE *kmsg=fopen("/dev/kmsg","a");
    //创建epoll 启动unit并且设置子进程io
    initor.epoll_fd=epoll_create1(EPOLL_CLOEXEC);
    //setfd_cloexec(fileno(initor.kmsg));
    // 注册signalfd
    sigset_t mask;
    sigemptyset(&mask);sigaddset(&mask,SIGCHLD);sigaddset(&mask,SIGTERM);
    sigaddset(&mask,SIGINT);sigaddset(&mask,SIGQUIT);
    sigaddset(&mask,SIGRTMIN+1); //child process ready
    sigaddset(&mask,SIGRTMIN+2); //未定义
    if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1){
        exit(EXIT_FAILURE);
    }
    int sfd=signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC);
    if (sfd<0)
    {
        exit(EXIT_FAILURE);
    }
    epmsg_t *epmsg=epmsg_init(sfd,EPOLLET|EPOLLIN,NULL,NULL);
    epmsg_add(initor.epoll_fd,epmsg);
     // 监听socket并且添加socket fd到epoll
    unlink(initor.sock_path);
    int listener=listen_unix(initor.sock_path,SOCK_STREAM,50);
    fdcloexec(listener);
    epmsg=epmsg_init(listener,EPOLLET|EPOLLIN,NULL,NULL);
    epmsg_add(initor.epoll_fd,epmsg);
    if (initor.kmsg!=NULL)
    {
        fprintf(initor.kmsg,"rinitd[1]: start server pid=%d cpu_total=%d\n",getpid(),get_nprocs());
        fflush(initor.kmsg);
    }
    //加载配置
    initor.bootm=bootm_init(initor.unit_path,"multi.target");
    struct epoll_event evts[20];
    // 注册 taskflow任务流
    int taskflow[2];
    if (pipe2(taskflow,O_CLOEXEC|O_NONBLOCK)<0)
    {
        exit(EXIT_FAILURE);
    }
    epmsg=epmsg_init(taskflow[0],EPOLLET|EPOLLIN,NULL,NULL); //读端
    epmsg_add(initor.epoll_fd,epmsg);
stoptarget:
    epmsg=epmsg_init(taskflow[1],EPOLLOUT,NULL,NULL); //写端
    epmsg_add(initor.epoll_fd,epmsg);
    // Target_t *init_target=unittarget_generate(initor.unit_path,"multi.target");
    // initor.now=init_target;
    // fprintf(kmsg,"rinitd[1]: epoll add listen fd=%d\n",listener);
    // fflush(kmsg);
    // epoll处理io,socket stdio
    
    while (1)
    {
        int ret=epoll_wait(initor.epoll_fd,evts,20,-1);
        for (int i = 0; i < ret; i++)
        {
            epmsg_t *evmsg=(epmsg_t*)evts[i].data.ptr;
            if ((evts[i].events&EPOLLIN)&&evmsg->fd==listener) //socket触发连接事件,根据请求协议判断类型
            {
                //socket新建连接
                struct sockaddr_un client_addr;
                int clen = sizeof(client_addr);
                int client_socket = accept(listener, (struct sockaddr*)&client_addr,(socklen_t*)&clen);
                fdcloexec(client_socket);
                head_t req_header;
                int headlen=sizeof(head_t);
                int ret=recv(client_socket,&req_header,headlen,SOCK_NONBLOCK);
                if (ret==headlen&&req_header.magic==CONN_MAGIC_CMD) //systemctl
                {   
                    //处理并关闭client socket并释放epoll ptr
                    if (cmd_action(evmsg->fd,req_header)==1)
                    {
                        printf("[\033[3;32mrinitd\033[0m] system shutdown\n");
                        goto shutdown;
                    }
                }
                // else if (ret==headlen&&req_header.magic==CONN_MAGIC_INOTIFY&&req_header.action==CONN_INOTIFY_READY) // task inotify
                // {
                //     Task_t *task=lookup_task(init_target,req_header.payloadlen,NULL);
                //     task->status.ready=1;
                // }
                close(client_socket);
            }else if ((evts[i].events&EPOLLIN)&&evmsg->fd==sfd) // signalfd信号处理
            {
                struct signalfd_siginfo fdsi;
                ssize_t s=read(evmsg->fd, &fdsi, sizeof(fdsi));
                if (s!=sizeof(fdsi))
                {
                    continue;
                }
                if (fdsi.ssi_signo==SIGCHLD) // 回收子进程
                {
                    sigcld_handler(&fdsi);
                }else if (fdsi.ssi_signo==SIGINT || fdsi.ssi_signo==SIGTERM || fdsi.ssi_signo==SIGQUIT) //退出
                {
                    unlink(initor.sock_path);
                    printf("[\033[3;32mrinitd\033[0m] Shutdown.\n");
                    initor.bootm->flag=-1;
                    goto stoptarget;
                    // exit(EXIT_SUCCESS);
                }else if (fdsi.ssi_signo==SIGRTMIN+1) //rtsig inotify process ready
                {
                    Task_t *task=lookup_task(initor.bootm->upper,fdsi.ssi_pid,NULL);
                    printf("[\033[3;32m  ok  \033[0m] Started %s -- runing \n",task->service.conf_file);
                    task->status.ready=1;
                }
                
                
            }else if ((evts[i].events&EPOLLIN)&&evmsg->fd==taskflow[0]) // taskflow 执行任务
            {
                int proto=0;
                int ret=read(evmsg->fd,&proto,sizeof(proto));
                if (ret!=sizeof(proto))
                {
                    continue;
                }
                if (proto==1) //start target
                {
                    Target_t *target=NULL;
                    ssize_t n=read(evmsg->fd,&target,sizeof(Target_t*));
                    if (n==sizeof(target))
                    {
                        start(target,pipe_handler);
                    }
                }else if (proto==-1) //stop target
                {
                    Target_t *target=NULL;
                    ssize_t n=read(evmsg->fd,&target,sizeof(Target_t*));
                    if (n==sizeof(target))
                    {
                        terminate(target);
                    }
                }
                
                
                
            }else if ((evts[i].events&EPOLLOUT)&&evmsg->fd==taskflow[1]) // taskflow 检查已分发任务状态,并且分发新任务
            {
                if (initor.bootm->flag==1) //start target
                {
                    //1.检查
                    if (initor.bootm->now==NULL) //分发完成移除taskflow写端
                    {
                        initor.bootm->now=initor.bootm->lower;
                        epoll_ctl(initor.epoll_fd,EPOLL_CTL_DEL,epmsg->fd,NULL);
                        free(epmsg);
                        continue;
                    }
                    Target_t *last=(Target_t*)initor.bootm->now->last;
                    if (last!=NULL)
                    {
                        if (check_status(last)<0) //last target 未就绪继续等待.
                        {   
                            // printf("[\033[3;34m wait \033[0m] Attaching %s -- Task queue=%d.\n",last->conf_file,last->taskslen);
                            // fflush(stdout);
                            continue;
                        }else{
                            printf("[\033[3;32m  ok  \033[0m] Attached %s -- Task queue=%d.\n",last->conf_file,last->taskslen);
                            fflush(stdout);
                        }
                    }
                    //2.分发 
                    write(evmsg->fd,&initor.bootm->flag,sizeof(initor.bootm->flag));
                    ssize_t ret=write(evmsg->fd,&initor.bootm->now,sizeof(Target_t*));
                    if (ret==sizeof(Target_t*)) //分发成功
                    {
                        printf("[\033[3;32m  ok  \033[0m] Attach %s -- Task queue len=%d.\n",initor.bootm->now->conf_file,initor.bootm->now->taskslen);
                        fflush(stdout);
                        initor.bootm->now=(Target_t*)initor.bootm->now->next;

                    }
                }else if (initor.bootm->flag==-1) //stop target
                {
                    //1.检查
                    if (initor.bootm->now==NULL) //分发完成移除taskflow写端
                    {
                        initor.bootm->now=initor.bootm->upper;
                        if (check_alive(initor.bootm->now)<0) //next target 未stop 继续等待.
                        {   
                            // printf("[\033[3;34m wait \033[0m] Attaching %s -- Task queue=%d.\n",last->conf_file,last->taskslen);
                            // fflush(stdout);
                            continue;
                        }else{
                            printf("[\033[3;32m  ok  \033[0m] Terminated %s -- Task queue=%d.\n",initor.bootm->now->conf_file,initor.bootm->now->taskslen);
                            fflush(stdout);
                            epoll_ctl(initor.epoll_fd,EPOLL_CTL_DEL,epmsg->fd,NULL);
                            free(epmsg);
                            goto shutdown;
                        }
                        continue;
                    }                    
                    Target_t *next=(Target_t*)initor.bootm->now->next;
                    if (next!=NULL)
                    {
                        if (check_alive(next)<0) //next target 未stop 继续等待.
                        {   
                            // printf("[\033[3;34m wait \033[0m] Attaching %s -- Task queue=%d.\n",last->conf_file,last->taskslen);
                            // fflush(stdout);
                            continue;
                        }else{
                            printf("[\033[3;32m  ok  \033[0m] Terminated %s -- Task queue=%d.\n",next->conf_file,next->taskslen);
                            fflush(stdout);
                        }
                    }
                    //2.分发 
                    write(evmsg->fd,&initor.bootm->flag,sizeof(initor.bootm->flag));
                    ssize_t ret=write(evmsg->fd,&initor.bootm->now,sizeof(Target_t*));
                    if (ret==sizeof(Target_t*)) //分发成功
                    {
                        printf("[\033[3;32m  ok  \033[0m] Terminate %s -- Task queue len=%d.\n",initor.bootm->now->conf_file,initor.bootm->now->taskslen);
                        fflush(stdout);
                        initor.bootm->now=(Target_t*)initor.bootm->now->last;

                    }
                }
                
                
                
            }
        }
    }
shutdown:
    while (1) //关机
    {
        terminate(initor.bootm->now);
        printf("[\033[3;32m  ok  \033[0m] Stop %s -- Task queue len=%d.\n",initor.bootm->now->conf_file,initor.bootm->now->taskslen);
        if (initor.bootm->now->last!=NULL)
        {
            initor.bootm->now=(Target_t*)initor.bootm->now->last;
        }else{
            break;
        }
    }
    unlink(initor.sock_path);
    //fclose(initor.kmsg);
    close(initor.epoll_fd);      //关闭epollfd
    //关机
    //reboot(RB_POWER_OFF);
    // 释放配置内存空间
    return 0; 
}



//rinitd客户端
int client(char *sock_path,char *cmd,char *name) {
    int server_socket=connect_unix(sock_path,SOCK_STREAM);
    if (server_socket == -1) {
        perror("Error:");
        exit(1);
    }
    trim(cmd); 
    if (name!=NULL)
    {
        trim(name);
        if (string_has_suffix(name,".target")!=1&&string_has_suffix(name,".servive")!=1)
        {
            strcat(name,".service");
        }
    }
    if (strcmp(cmd,"stop")==0 || strcmp(cmd,"start")==0)
    {   
        CONN_t gma;
        gma.header.magic=CONN_MAGIC_CMD;
        gma.header.action=CONN_CMD_START;
        strcpy(gma.data,name);
        printf("name:%s\n",gma.data);
        gma.header.payloadlen=strlen(name)+1;
        printf("byte:%d\n",gma.header.payloadlen-1);
        write(server_socket,&gma.header,12);
        write(server_socket,gma.data,gma.header.payloadlen);
    }else if (strcmp(cmd,"status")==0)
    {
        CONN_t gma;
        gma.header.magic=CONN_MAGIC_CMD;
        gma.header.action=CONN_CMD_STATUS;
        strcpy(gma.data,name);
        printf("name:%s\n",gma.data);
        gma.header.payloadlen=strlen(name)+1;
        printf("byte:%d\n",gma.header.payloadlen-1);
        write(server_socket,&gma.header,12);
        write(server_socket,gma.data,gma.header.payloadlen);
    }else if (strcmp(cmd,"shutdown")==0)
    {
        head_t head;
        head.magic=CONN_MAGIC_CMD;
        head.action=CONN_CMD_SHUTDOWN;
        write(server_socket, &head, 6);
    }
    close(server_socket);
    exit(0);
}
